package Parser;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.StringTokenizer;

import org.broad.igv.bbfile.BBFileReader;
import org.broad.igv.bbfile.BedFeature;
import org.broad.igv.bbfile.BigBedIterator;
import org.broad.igv.bbfile.BigWigIterator;
import org.broad.igv.bbfile.RPChromosomeRegion;
import org.broad.igv.bbfile.WigItem;

import net.sf.picard.sam.BuildBamIndex;
import net.sf.picard.sam.FilterSamReads;
import net.sf.picard.sam.ViewSam;
import net.sf.samtools.SAMFileHeader;
import net.sf.samtools.SAMFileReader;
import net.sf.samtools.SAMRecord;
import net.sf.samtools.SAMRecordIterator;
import net.sf.samtools.SAMSequenceRecord;
import net.sf.samtools.SAMTools;

import DataModel.Feature;
import DataModel.FeatureHolder;
import DataModel.Tupel;
import Interfaces.SortableElements;
import SortSearch.QuickSorter;

/**
 * This class implements a parser for file formats only containing features.<br>
 * See also:<br>
 * <a href="http://genome.ucsc.edu/goldenPath/help/wiggle.html">http://genome.
 * ucsc.edu/goldenPath/help/wiggle.html</a><br>
 * <a href="http://genome.ucsc.edu/goldenPath/help/bedgraph.html">http://genome.
 * ucsc.edu/goldenPath/help/bedgraph.html</a><br>
 * <a
 * href="http://genome.ucsc.edu/FAQ/FAQformat.html#format3">http://genome.ucsc
 * .edu/FAQ/FAQformat.html#format3</a><br>
 * <a
 * href="http://genome.ucsc.edu/FAQ/FAQformat.html#format1">http://genome.ucsc
 * .edu/FAQ/FAQformat.html#format1</a><br>
 * <a
 * href="http://samtools.sourceforge.net/SAM1.pdf">http://samtools.sourceforge
 * .net/SAM1.pdf</a> <a
 * href="http://genome.ucsc.edu/FAQ/FAQformat.html#format4">
 * http://genome.ucsc.edu/FAQ/FAQformat.html#format4</a>
 * 
 * @author Stefan Lorenz<br>
 * <br>
 *         26.11.2012
 */
public class ParseFeatureFormat {
	private File file;
	private FeatureHolder list;
	private String reference;
	private String range;

	/**
	 * Constructor of the parser
	 * 
	 * @param file
	 *            File to parse
	 */
	public ParseFeatureFormat(File file, String reference, String range) {
		this.reference = reference;
		this.range = range;
		this.file = file;
		this.list = new FeatureHolder();
		this.list.setFile(file);
		readFile();
	}

	/**
	 * Reads the file extension and decides the parsing method and sorts the
	 * {@link Feature}s
	 */
	private void readFile() {
		String extension = this.file.getName().substring(this.file.getName().lastIndexOf(".") + 1).toUpperCase();
		if (extension.contains("GFF")) {
			this.readGff();
			this.list.setTypeOfFile("gff");
		} else if (extension.contains("BEDGRAPH")) {
			this.readWig();
			this.list.setTypeOfFile("wig");
		} else if (extension.contains("WIG")) {
			this.readWig();
			this.list.setTypeOfFile("wig");
		} else if (extension.contains("BED")) {
			this.readBed();
			this.list.setTypeOfFile("bed");
		} else if (extension.contains("BB")) {
			this.readBigBed();
			this.list.setTypeOfFile("bed");
		} else if (extension.contains("SAM")) {
			this.readSam();
			this.list.setTypeOfFile("sam");
		} else if (extension.contains("BAM")) {
			this.readSam();
			this.list.setTypeOfFile("sam");
		} else if (extension.contains("GTF")) {
			this.readGTF();
			this.list.setTypeOfFile("gtf");
		} else if (extension.contains("BW")) {
			this.readBigWig();
			this.list.setTypeOfFile("wig");
		}

		// if the list contains more than one feature it must get sorted
		// important for a stabil binary search afterwards
		if (this.list.size() > 1) {
			SortableElements[] tmpArray = new SortableElements[this.list.size()];
			for (int i = 0; i < tmpArray.length; i++) {
				tmpArray[i] = this.list.get(i);
			}
			QuickSorter sorter = new QuickSorter(tmpArray);
			sorter.run();
			this.list.clear();
			for (int i = 0; i < sorter.getA().length; i++) {
				this.list.add(i, (Feature) sorter.getA()[i]);
			}
		}
	}

	/**
	 * Read bigbed file
	 */
	private void readBigBed() {
		BBFileReader bbf;
		try {
			bbf = new BBFileReader(this.file.getAbsolutePath());
			if (!bbf.isBigBedFile())
				return;
			int id = bbf.getChromosomeID(this.reference);
			RPChromosomeRegion bounds = bbf.getChromosomeBounds(id, id);

			Tupel position = getRangeToParseFromString(this.range);
			BigBedIterator iterator;
			if (position.getStart() == -1 && position.getStop() == -1)
				iterator = bbf.getBigBedIterator(this.reference, 0, this.reference, bounds.getEndBase(), true);
			else
				iterator = bbf.getBigBedIterator(this.reference, position.getStart(), this.reference, position.getStop(), true);
			while (iterator.hasNext()) {

				BedFeature tmp = iterator.next();
				Feature f = new Feature();
				Tupel tupel = new Tupel(tmp.getStartBase(), tmp.getEndBase());
				f.setAbsoultePositions(tupel);
				f.setLocus(tmp.getChromosome());
				f.setType("bed");
				f.setName("BigBed");

				String[] blockLength = null;
				String[] blockStart = null;
				for (int i = 0; i < tmp.getRestOfFields().length; i++) {
					switch (i) {
					case 1:
						f.setScore(Float.parseFloat(tmp.getRestOfFields()[i]));
						break;
					case 2:
						f.setStrand(tmp.getRestOfFields()[i]);
						break;
					case 6:
						for (int j = 0; j < Integer.parseInt(tmp.getRestOfFields()[i]); j++) {
							f.getListOfPositions().add(new Tupel());
						}
						break;
					case 7:
						blockLength = tmp.getRestOfFields()[i].split(",");
						break;
					case 8:
						blockStart = tmp.getRestOfFields()[i].split(",");
						break;
					default:
						break;
					}
				}
				ArrayList<Tupel> tupelList = new ArrayList<Tupel>();
				for (int i = 0; i < blockStart.length; i++) {
					tupelList.add(new Tupel(f.getStart() + Integer.valueOf(blockStart[i]), f.getStart() + Integer.valueOf(blockStart[i]) + Integer.valueOf(blockLength[i])));
				}
				f.setListOfPositions(tupelList);
				this.list.add(f);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Read bigwig file
	 */
	private void readBigWig() {
		BBFileReader bbf;
		try {
			bbf = new BBFileReader(this.file.getAbsolutePath());
			if (!bbf.isBigWigFile())
				return;
			int id = bbf.getChromosomeID(this.reference);
			RPChromosomeRegion bounds = bbf.getChromosomeBounds(id, id);

			Tupel position = getRangeToParseFromString(this.range);
			BigWigIterator iterator;
			if (position.getStart() == -1 && position.getStop() == -1)
				iterator = bbf.getBigWigIterator(this.reference, 0, this.reference, bounds.getEndBase(), true);
			else
				iterator = bbf.getBigWigIterator(this.reference, position.getStart(), this.reference, position.getStop(), true);

			while (iterator.hasNext()) {
				WigItem tmp = iterator.next();
				Feature f = new Feature();
				Tupel tupel = new Tupel(tmp.getStartBase(), tmp.getEndBase());
				f.setAbsoultePositions(tupel);
				f.addTupelToListOfPositions(tupel);
				f.setLocus(tmp.getChromosome());
				f.setType("wig");
				f.setName("BigWig");
				f.setStrand("+");
				f.setScore(tmp.getWigValue());
				this.list.add(f);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Read wig and bedgraph file
	 */
	private void readWig() {
		try {

			Tupel position = getRangeToParseFromString(this.range);
			FileReader gffReader = new FileReader(this.file.getAbsolutePath());
			BufferedReader bGffReader = new BufferedReader(gffReader);
			String currentLine = "";
			int start = 0;
			int span = 1;
			int step = 1;
			String ref = "";
			String type = "";
			boolean isVariableStep = false;
			Tupel tempTupel = new Tupel(-1, -1);
			int tempScore = -1;
			int count = -1;
			while ((currentLine = bGffReader.readLine()) != null) {
				StringTokenizer tokenizer = new StringTokenizer(currentLine);
				if (currentLine.startsWith("browser")) {
					continue;
				}
				if (currentLine.startsWith("track")) {
					while (tokenizer.hasMoreTokens()) {
						String next = tokenizer.nextToken();
						if (next.contains("type=")) {
							type = next.split("=")[1].toString();
						}
					}
					continue;
				}
				// specify settings for type wiggle
				if (type.contains("wiggle") && (currentLine.contains("variableStep") | currentLine.contains("fixedStep"))) {
					if (currentLine.contains("variableStep"))
						isVariableStep = true;
					if (currentLine.contains("fixedStep"))
						isVariableStep = false;
					while (tokenizer.hasMoreTokens()) {
						String next = tokenizer.nextToken();
						if (next.contains("step=")) {
							step = Integer.parseInt(next.split("=")[1]);
						} else if (next.contains("start=")) {
							start = Integer.parseInt(next.split("=")[1]);
						} else if (next.contains("span=")) {
							span = Integer.parseInt(next.split("=")[1]);
						} else if (next.contains("chrom=")) {
							ref = next.split("=")[1].toString();
						}
					}
					type = "wig";
					System.out.println(isVariableStep + " is varabler schritt");
					continue;
				}

				// specify settings for type bedgraph
				else if (type.toLowerCase().contains("bedgraph") && this.reference.equals(ref)) {
					if (!tokenizer.nextToken().equals(this.reference))
						continue;
					int startPos = Integer.parseInt(tokenizer.nextToken());
					int stopPos = Integer.parseInt(tokenizer.nextToken());
					// add if complete ist selected or the start and stop
					// position is in range
					if ((position.getStart() == -1 && position.getStop() == -1) || (startPos <= position.getStop() && stopPos > position.getStart())) {

						Feature f = new Feature();
						Tupel tupel = new Tupel(startPos, stopPos);
						f.setAbsoultePositions(tupel);
						ArrayList<Tupel> tupelList = new ArrayList<Tupel>();
						tupelList.add(new Tupel(startPos, stopPos));
						f.setListOfPositions(tupelList);

						f.setLocus("");
						f.setType("wig");
						f.setStrand("");
						f.setName("");
						f.setScore((int) Float.parseFloat(tokenizer.nextToken()));
						this.list.add(f);

						count++;
						continue;
					}
				}
				// normal wiggle plot
				else if (type.contains("wig") && this.reference.equals(ref)) {
					// variable step wig
					if (isVariableStep) {
						int pos = Integer.parseInt(tokenizer.nextToken());
						int score = (int) Float.parseFloat(tokenizer.nextToken());

						if (score == tempScore) {
							this.list.get(this.list.size() - 1).getListOfPositions().get(this.list.get(this.list.size() - 1).getListOfPositions().size() - 1).setStop(start + pos + span);
							this.list.get(this.list.size() - 1).getAbsoultePositions().setStop(start + pos + span);
						} else {
							tempTupel.setStart(start + pos);
							tempTupel.setStop(start + pos + span);
							tempScore = score;

							// add if complete ist selected or the start and
							// stop position is in range
							if ((position.getStart() == -1 && position.getStop() == -1) || (start + pos <= position.getStop() && start + pos + span > position.getStart())) {

								Feature f = new Feature();
								Tupel tupel = new Tupel(start + pos, start + pos + span);
								f.setAbsoultePositions(tupel);
								ArrayList<Tupel> tupelList = new ArrayList<Tupel>();
								tupelList.add(tupel);
								f.setListOfPositions(tupelList);
								f.setScore(score);
								f.setLocus("");
								f.setType("wig");
								f.setStrand("");
								f.setName("");
								this.list.add(f);
								count++;
							}
						}
					}
					// fixed step wig
					else {
						if ((position.getStart() == -1 && position.getStop() == -1) || (start + (count * step) <= position.getStop() && start + (count * step) + span > position.getStart())) {

							Feature f = new Feature();
							f.setScore((int) Float.parseFloat(tokenizer.nextToken()));
							Tupel tupel = new Tupel(start + (count * step), start + (count * step) + span);
							f.setAbsoultePositions(tupel);
							ArrayList<Tupel> tupelList = new ArrayList<Tupel>();
							tupelList.add(tupel);
							f.setListOfPositions(tupelList);

							f.setLocus("");
							f.setType("wig");
							f.setStrand("");
							f.setName("");
							this.list.add(f);
							count++;
						}
					}
					continue;
				}
			}
			bGffReader.close();
			gffReader.close();
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Read gff file
	 * 
	 * @throws IOException
	 */
	private void readGff() {

		try {
			FileReader gffReader = new FileReader(this.file.getAbsolutePath());
			Tupel position = getRangeToParseFromString(this.range);
			BufferedReader bGffReader = new BufferedReader(gffReader);

			String currentLine = "";
			String header = "";
			String chr = "";
			int start = 0;
			int end = 0;
			float score = 0;
			String isStrand = "";
			String type = "";
			while ((currentLine = bGffReader.readLine()) != null) {
				if (currentLine.startsWith("#"))
					continue;
				StringTokenizer tokenizer = new StringTokenizer(currentLine);
				chr = tokenizer.nextToken();
				if (!chr.equals(this.reference))
					continue;

				header = tokenizer.nextToken();

				type = tokenizer.nextToken();

				start = Integer.valueOf(tokenizer.nextToken());
				end = Integer.valueOf(tokenizer.nextToken());

				String s = tokenizer.nextToken();
				if (s.equals("."))
					score = 0;
				else
					score = Float.valueOf(s);

				isStrand = "+";
				if (tokenizer.nextToken().equals("-"))
					isStrand = "-";

				if ((position.getStart() == -1 && position.getStop() == -1) || (start <= position.getStop() && end > position.getStart())) {

					Feature f = new Feature();
					Tupel tupel = new Tupel(start, end);
					f.setAbsoultePositions(tupel);
					ArrayList<Tupel> tupelList = new ArrayList<Tupel>();
					tupelList.add(tupel);
					f.setListOfPositions(tupelList);
					f.setLocus(chr);
					f.setType(type);
					f.setStrand(isStrand);
					f.setScore(score);
					f.setName(header);
					ArrayList<String> additionalList = new ArrayList<String>();
					additionalList.add(header);
					additionalList.add(type);

					try {
						tokenizer.nextToken();
						additionalList.add(tokenizer.nextToken().toString());
					} catch (Exception e) {
						e.fillInStackTrace();
					}

					f.setAdditionalInformation(additionalList);
					this.list.add(f);
				}
			}

			bGffReader.close();
			gffReader.close();
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Read bed file
	 * 
	 * @throws IOException
	 */
	private void readBed() {
		try {
			FileReader gffReader = new FileReader(this.file.getAbsolutePath());
			BufferedReader bGffReader = new BufferedReader(gffReader);
			Tupel position = getRangeToParseFromString(this.range);

			String currentLine = "";
			String header = "";
			String chr = "";
			int start = 0;
			int end = 0;
			int score = 0;
			String isStrand = "";

			while ((currentLine = bGffReader.readLine()) != null) {
				if (currentLine.equals(""))
					continue;

				String[] tokenizer = currentLine.split("\t");
				chr = tokenizer[0];

				if (!chr.equals(this.reference))
					continue;

				start = Integer.valueOf(tokenizer[1]);
				end = Integer.valueOf(tokenizer[2]);
				
				

				if ((position.getStart() == -1 && position.getStop() == -1) || (start <= position.getStop() && end >= position.getStart())) {
					
					Feature f = new Feature();
					Tupel tupel = new Tupel(start, end);
					f.setAbsoultePositions(tupel);

					if (tokenizer.length == 3) {
						f.getListOfPositions().add(tupel);
						score = 0;
						isStrand = "+";
						header = "NA";
					}
					if (tokenizer.length >= 4)
						header = tokenizer[3];
					else
						header = "NA";
					
					if (tokenizer.length >= 5)
						score = Integer.valueOf(tokenizer[4]);
					else
						score = 0;
					if (tokenizer.length >= 6)
						if (tokenizer[5].contains("+")) {
							isStrand = "+";
						} else {
							isStrand = "-";
						}
					else
						isStrand = "+";

					if (tokenizer.length >= 10 && Integer.parseInt(tokenizer[9].toString()) > 1) {
						String[] blockLength = tokenizer[10].split(",");
						String[] blockStart = tokenizer[11].split(",");

						ArrayList<Tupel> tupelList = new ArrayList<Tupel>();
						for (int i = 0; i < blockStart.length; i++) {
							tupelList.add(new Tupel(start + Integer.valueOf(blockStart[i]), start + Integer.valueOf(blockStart[i]) + Integer.valueOf(blockLength[i])));
						}
						f.setListOfPositions(tupelList);
					} else if (tokenizer.length >= 10 && Integer.parseInt(tokenizer[9].toString()) <= 1) {
						ArrayList<Tupel> tupelList = new ArrayList<Tupel>();

						tupelList.add(new Tupel(start, end));
						f.setListOfPositions(tupelList);

					}
					f.setLocus(chr);
					f.setType("bed");
					f.setStrand(isStrand);
					f.setScore(score);
					f.setName(header);
					ArrayList<String> additionalList = new ArrayList<String>();
					f.setAdditionalInformation(additionalList);
					this.list.add(f);
				}
			}			
			bGffReader.close();
			gffReader.close();
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Read SAM/BAM file
	 * 
	 * @throws IOException
	 */
	private void readSam() {
		SAMFileReader samReader = new SAMFileReader(this.file);
		Tupel position = getRangeToParseFromString(this.range);
		if (!samReader.hasIndex()) {
			File index = new File(this.file.getAbsolutePath().substring(0, this.file.getAbsolutePath().length() - 4) + ".bai");
			BuildBamIndex.createIndex(samReader, index);
			samReader.enableIndexCaching(true);
		}
		samReader.close();
		samReader = new SAMFileReader(this.file);

		SAMRecordIterator iterator;
		if (position.getStart() == -1 && position.getStop() == -1)
			iterator = samReader.query(this.reference, 0, samReader.getFileHeader().getSequence(this.reference).getSequenceLength(), true);
		else {
			iterator = samReader.query(this.reference, position.getStart(), position.getStop(), true);
		}
		while (iterator.hasNext()) {
			SAMRecord samRecord = iterator.next();
			Feature f = new Feature();
			f.setName(samRecord.getReadName());
			f.setLocus(samRecord.getReferenceName());
			f.setScore(samRecord.getMappingQuality());

			Tupel tupel = new Tupel(samRecord.getAlignmentStart(), samRecord.getAlignmentEnd());
			f.setAbsoultePositions(tupel);
			ArrayList<Tupel> tupelList = new ArrayList<Tupel>();

			for (int i = 0; i < samRecord.getAlignmentBlocks().size(); i++) {
				tupelList.add(new Tupel(samRecord.getAlignmentBlocks().get(i).getReferenceStart(), samRecord.getAlignmentBlocks().get(i).getReferenceStart()+samRecord.getAlignmentBlocks().get(i).getLength()-1));
			}			
			f.setListOfPositions(tupelList);
			
			if (samRecord.getReadNegativeStrandFlag())
				f.setStrand("-");
			else
				f.setStrand("+");
			f.setType("sam");

			ArrayList<String> additionalList = new ArrayList<String>();
			additionalList.add("/MD=\"" + samConvertMDTag(samRecord) + "\"");
			// additionalList.add("/Seq=\"" + samRecord.getReadString() + "\"");
			f.setAdditionalInformation(additionalList);

			this.list.add(f);
		}
		samReader.close();
	}

	private String samConvertMDTag(SAMRecord samRecord) {
		String md = samRecord.getStringAttribute("MD");
		String result = "";
		try{
		if (md != null) {
			String seq = "";
			for (int x = 0; x < md.length(); x++) {
				if (isNumber(md.substring(x, x + 1))) {
					seq += md.substring(x, x + 1);
				} else
					seq += ":" + md.substring(x, x + 1) + ":";
			}
			String[] splt = seq.split(":");

			int pos = 0;
			result = "";
			for (String part : splt) {
				if (part.equals("0"))
					continue;
				else if (isNumber(part)) {
					result += part + ",";
					if (Integer.parseInt(part) > 0)
						pos = Integer.parseInt(part) - 1;
				} else {
					result += samRecord.getReadString().charAt(pos + 1) + ",";
					pos += 1;
				}
			}
		}
		}catch(Exception e){
			
		}
		return result;
	}

	/**
	 * Parses a gtf file
	 * 
	 * @throws IOException
	 */
	private void readGTF() {
		try {
			System.out.println("reads");
			BufferedReader br = new BufferedReader(new FileReader(this.file));
			String currentLine = "";
			Tupel position = getRangeToParseFromString(this.range);

			while ((currentLine = br.readLine()) != null) {
				if (currentLine.equals("") | currentLine.startsWith("##"))
					continue;
				String[] tab = currentLine.split("\t");

				if (!tab[0].toString().equals(this.reference))
					continue;
				
				int start = Integer.parseInt(tab[3]);
				int end = Integer.parseInt(tab[4]);
				if ((position.getStart() == -1 && position.getStop() == -1) || (start <= position.getStop() && end > position.getStart())) {

					Feature f = new Feature();
					f.setLocus(tab[0].toString());
					f.setType(tab[2].toString());

					f.setAbsoultePositions(new Tupel(Integer.parseInt(tab[3]), Integer.parseInt(tab[4])));
					f.getListOfPositions().add(new Tupel(Integer.parseInt(tab[3]), Integer.parseInt(tab[4])));

					if (!tab[6].toString().equals("."))
						f.setStrand(tab[6].toString());
					else
						f.setStrand("+");

					if (!tab[7].equals(".")) {
						f.getAdditionalInformation().add("/note=\"" + tab[7] + "\"");
					}
					try {
						String[] attributes = tab[8].split(";");

						for (int i = 0; i < attributes.length; i++) {

							String[] split = attributes[i].trim().split(" ");

							String infoAdd = "";
							for (int j = 1; j < split.length; j++) {
								infoAdd += split[j] + " ";
							}
							infoAdd = infoAdd.trim();
							f.getAdditionalInformation().add("/" + split[0] + "=" + infoAdd + "");

							if (f.getType().equals("gene") && split[0].equals("gene_id")) {
								f.setName(split[1].replace("\"", ""));
							}
							if (f.getType().equals("transcript") && split[0].equals("transcript_id")) {
								f.setName(split[1].replace("\"", ""));
							}
							if (f.getType().equals("exon") && split[0].equals("transcript_id")) {
								f.setName(split[1].replace("\"", ""));
							}
							if (f.getType().equals("CDS") && split[0].equals("transcript_id")) {
								f.setName(split[1].replace("\"", ""));
							}

							if (split[0].equals("FPKM")) {
								f.setScore(Float.parseFloat(split[1].replace("\"", "")));
							}
						}
					} catch (Exception e) {
					}

					if (f.getType().equals("gene")) {
						this.list.add(f);
					}
					if (f.getType().equals("transcript")) {
						this.list.add(f);
						if (f.getAdditionalInformation().contains("/transcript_type=\"protein_coding\"")) {
							Feature cds_template = new Feature();
							cds_template.setLocus(f.getLocus());
							cds_template.setType("CDS");
							cds_template.setAbsoultePositions(new Tupel(0, 0));
							cds_template.getListOfPositions().add(new Tupel(0, 0));
							cds_template.setName(f.getName().toString());
							cds_template.setScore(0);
							cds_template.setStrand(f.getStrand());
							this.list.add(cds_template);
						}

					}
					if (f.getType().equals("exon")) {

						// found transcript with right transcript ID
						for (int i = this.list.size() - 1; i >= 0; i--) {
							if (this.list.get(i).getName().equals(f.getName()) && this.list.get(i).getType().equals("transcript")) {
								// if the first tupel is equal to the absolut
								// position -> change otherise ad new Tupel
								if (this.list.get(i).getListOfPositions().get(0).getStart() == this.list.get(i).getStart() && this.list.get(i).getListOfPositions().get(0).getStop() == this.list.get(i).getStop()) {
									this.list.get(i).getListOfPositions().remove(0);
								}
								if (f.getStrand().equals("+"))
									this.list.get(i).getListOfPositions().add(f.getListOfPositions().get(0));
								else
									this.list.get(i).getListOfPositions().add(f.getListOfPositions().get(0));
								break;
							}
						}
					}
					if (f.getType().equals("CDS")) {
						// found protein template with right transcript ID
						for (int i = this.list.size() - 1; i >= 0; i--) {
							if (this.list.get(i).getName().equals(f.getName()) && this.list.get(i).getType().equals("CDS")) {

								if (0 == this.list.get(i).getStart() && 0 == this.list.get(i).getStop()) {
									this.list.get(i).getListOfPositions().remove(0);
									this.list.get(i).getListOfPositions().add(f.getListOfPositions().get(0));
									this.list.get(i).setAbsoultePositions(f.getListOfPositions().get(0));
								}
								if (f.getStrand().equals("+")) {
									this.list.get(i).getListOfPositions().add(f.getListOfPositions().get(0));
									this.list.get(i).setAbsoultePositions(new Tupel(this.list.get(i).getStart(), f.getListOfPositions().get(0).getStop()));
								} else {
									this.list.get(i).getListOfPositions().add(0, f.getListOfPositions().get(0));
									this.list.get(i).setAbsoultePositions(new Tupel(f.getListOfPositions().get(0).getStart(), this.list.get(i).getStop()));
								}
								break;
							}
						}
						this.list.add(f);
					}
				}
			}
			br.close();
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * parses an region string which must contain either "-" or ":" as seperator
	 * of two INTs
	 * 
	 * @param region
	 *            to load into browser
	 * @return
	 */
	private Tupel getRangeToParseFromString(String region) {
		String range = region.replace(" ", "");
		Tupel tupel = new Tupel(-1, -1);
		if (range.toLowerCase() != "complete") {
			// sort order
			int start = -1;
			int stop = -1;
			if (range.contains("-")) {
				start = Integer.parseInt(range.split("-")[0]);
				stop = Integer.parseInt(range.split("-")[1]);
			} else if (range.contains(":")) {
				start = Integer.parseInt(range.split(":")[0]);
				stop = Integer.parseInt(range.split(":")[1]);
			}
			tupel.setStart(Math.min(start, stop));
			tupel.setStop(Math.max(start, stop));
		}
		return tupel;
	}

	/**
	 * Checks if a Str is a number
	 * 
	 * @param str
	 *            string to check
	 * @return true if it is a number false otherwise
	 */
	public boolean isNumber(String str) {
		try {
			Integer.parseInt(str);
			return true;
		} catch (NumberFormatException e) {
			return false;
		}
	}

	/**
	 * Returns the Feature holder
	 * 
	 * @return list of features
	 */
	public FeatureHolder getList() {
		return list;
	}
}
